package com.efeichong.lock;

import com.efeichong.exception.BaseException;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

import static com.efeichong.lock.Lock.*;

/**
 * @author lxk
 * @date 2020/02/04
 * @description 基于线程的重入锁
 */
@Slf4j
public class EnhanceReentrantLock {
    /**
     * 锁
     */
    private static final Map<String, ReentrantLock> LOCK_MAP = new HashMap<>();
    /**
     * 是否公平锁 否非公平锁  是公平锁,默认使用非公平锁
     */
    private boolean fair = false;

    public EnhanceReentrantLock() {

    }

    public EnhanceReentrantLock(boolean fair) {
        this.fair = fair;
    }

    /**
     * 加锁
     */
    public void lock(String key) {
        lock(key, LOCK_EXPIRE_TIME);
    }

    /**
     * 加锁
     *
     * @param lockExpireTime 锁自动过期时长 单位：毫秒
     */
    public void lock(String key, long lockExpireTime) {
        lock(key, lockExpireTime, RETRY_COUNT, RETRY_TIME_INTERVAL);
    }

    /**
     * 加锁
     *
     * @param retryCount 自动重试次数
     */
    public void lock(String key, int retryCount, long retryTimeInterval) {
        lock(key, LOCK_EXPIRE_TIME, retryCount, retryTimeInterval);
    }

    /**
     * 加锁
     *
     * @param lockExpireTime 锁自动过期时长 单位：毫秒
     * @param retryCount     自动重试次数
     */
    @SneakyThrows
    public void lock(String key, long lockExpireTime, int retryCount, long retryTimeInterval) {
        ReentrantLock lock;
        synchronized (EnhanceReentrantLock.class) {
            lock = LOCK_MAP.get(key);
            if (lock == null) {
                lock = new ReentrantLock(fair);
                LOCK_MAP.put(key, lock);
            }
        }
        if (lockExpireTime > 0) {
            ScheduledExecutorProvider.executor.schedule(new Runnable() {
                @Override
                public void run() {
                    LOCK_MAP.remove(key);
                }
            }, lockExpireTime, TimeUnit.MILLISECONDS);
        }


        if (retryCount < 1) {
            if (lock.tryLock()) {
                return;
            }
        } else {
            int retryTimes = 1;
            while (true) {
                if (retryTimes > retryCount) {
                    log.warn("重试{}次后未拿到锁", retryTimes - 1);
                    throw new BaseException("请勿重复进行该操作");
                }
                if (lock.tryLock()) {
                    return;
                }
                Thread.sleep(retryTimeInterval);
                retryTimes++;
            }
        }
        log.warn("请勿重复进行该操作");
        throw new BaseException("请勿重复进行该操作");
    }

    /**
     * 释放锁
     */
    public void unlock(String key) {
        ReentrantLock lock = LOCK_MAP.get(key);
        try {
            if (lock == null) {
                log.warn("非持锁线程，无法进行释放锁操作");
                return;
            }
            lock.unlock();
        } catch (IllegalMonitorStateException e) {
            log.warn(e.getMessage());
            throw new BaseException(e);
        }
    }
}
